#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# # author : cypro666
# # date   : 2015.08.01
import sys, pprint
from _collections import defaultdict
from math import log2
import numpy
from numpy.linalg import norm
from numpy import random, std
from optparse import OptionParser

from magic3.bivtable import *

def class_name(name):
    ''' return object's class name '''
    return str(name).split('.')[-1][:-2]

def help_for_io(k):
    if k == 'train_file':
        h = 'csv file name of train data'
    elif k == 'label_file':
        h = 'csv file name of label data for train data'
    elif k == 'testing_file':
        h = 'csv file name of testing data'
    elif k == 'results_file':
        h = 'file name to store results of testing data'
    elif k == 'log_file':
        h = 'log file name with full path'
    else:
        h = 'parameters for algorithm'
    return h

def make_options(options_names):
    optparser = OptionParser()
    optparser.set_usage('python3 -O %s [OPTIONS]' % sys.argv[0])

    for k, t, c in options_names:
        if t == 'choice' and c:
            optparser.add_option('--' + k, type=t, help=help_for_io(k), choices=c)
        else:
            optparser.add_option('--' + k, type=t, help=help_for_io(k))

    (options, args) = optparser.parse_args()
    options = options.__dict__
    parameters = {}
    
    if 'log_file' not in options or not options['log_file']:
        options['log_file'] = 'skt.log'
    
    for k, t, c in options_names:
        if not options[k]:
            optparser.error('%s, see help for more details' % k)
        parameters[k] = options[k]

    if __debug__:
        pprint.pprint(parameters)

    return parameters

def load_data(fname, convert=int):
    ''' read from the csv file and yields a tuple '''
    csv = CSV()
    csv.read(fname, ',', withhead=0, strip=1, convert=convert, encoding='utf-8-sig')
    for row in csv:
        yield tuple(row)

def _normalize(mat, method='std'):
    ''' `mat`: numpy.mat or 2D-array
        `method` : 'norm', 'std' '''
    try:
        m, n = numpy.shape(mat)
    except ValueError:
        try:
            m, n = 0, numpy.shape(mat)[0]
        except IndexError:
            return mat

    if not m:
        if method == 'std':
            valmax, valmin = mat.max(), mat.min()
            mat -= valmin
            mat /= (valmax - valmin)
        elif method == 'norm':
            mat /= norm(mat)

    else:
        if method == 'std':
            for i in range(m):
                valmax, valmin = mat[i].max(), mat[i].min()
                mat[i] -= valmin
                mat[i] /= (valmax - valmin)
        elif method == 'norm':
            for i in range(m):
                mat[i] /= norm(mat[i])

    return mat

def diff(a, b):
    ''' different between matrix a and b '''
    return norm(a - b)

def unique_counts(vec):
    ''' helper of gini_impurity and entropy '''
    c = defaultdict(lambda:0)
    for i in vec:
        c[i] += 1
    return c

def gini_impurity(vec):
    ''' caculate gini impurity value of a row '''
    total = float(len(vec))
    uc = unique_counts(vec)

    cache = tuple(n / total for n in uc.values())
    size = len(uc)
    ret = 0

    for i in range(size):
        for j in range(size):
            if i != j:
                ret += cache[i] * cache[j]

    return ret

def entropy(vec):
    ''' entropy is the sum of p(x) * log(p(x)) of the row '''
    counts = unique_counts(vec)
    ent = 0.0

    for v in counts.values():
        px = float(v) / len(vec)
        ent -= px * log2(px)

    return ent


if __name__ == '__main__':
    a = random.rand(4, 4)
    b = numpy.eye(4, 4) * 0.5

    print('Norm a - b:', diff(a, b))

    a = numpy.array([2, 2, 3, 4, 5, 2, 2, 1, 2, 2, 2, 2, 3, 7, 8, 9, 0], dtype=float)
    print('unique_counts:', dict(unique_counts(a)))
    print('gini_impurity:', gini_impurity(a))
    print('entropy:', entropy(a))
    print()

    a = numpy.array([1, 2, 3, 4, 5, 0, 3, 2, 1, 4], dtype=float)
    print('normalize:\n', _normalize(a, 'std'))
    print('normalize:\n', _normalize(a, 'norm'))
    print()

    a = numpy.array([[1, 2, 3, 4], [5, 4, 3, 2], [-2, 3, 4, 1]], dtype=float)
    print('normalize:\n', _normalize(a, 'std'))
    print('normalize:\n', _normalize(a, 'norm'))

    import time
    print(time.time())

    a = numpy.array(random.rand(100000))
    for i in range(500):
        a.sort()

    print(time.time())

